﻿-- 月度活动 元宝消耗排行榜
--#include "data\functions\ActorEvent\OfflineMsg.lua" once
--#include "data\config\activity\PublicTestCfg.lua" once

YbConsumeRanking = {}
YbConsumeRanking_minYb = 0
YbConsumeRanking_isModified = false
YbConsumeRanking_inTime = false

function IsYbConsumeRankingTime()	
	if PublicTestCfg.gameIndex ~= 0 and System.GetGameIndex() ~= PublicTestCfg.gameIndex then
		return false -- 月度活动不是该平台
	end
		
	local time = PublicTestCfg.time
	return System.isInDateRange(time[1], time[2], time[3], 0, 0, 0, time[4], time[5], time[6], 23, 59, 59)
end

function YbConsumeRanking_load()
	-- 注册离线消息处理器
	OfflineMsgFuncs[OfflineMsgIds.YbConsumeRanking1] = OfflineMsgGiveYbConsumeRankingAward
	OfflineMsgFuncs[OfflineMsgIds.YbConsumeRanking2] = OfflineMsgGiveYbConsumeRankingAward
	OfflineMsgFuncs[OfflineMsgIds.YbConsumeRanking3] = OfflineMsgGiveYbConsumeRankingAward
	
	YbConsumeRanking = {} -- 清空
	YbConsumeRanking_minYb = 0
	YbConsumeRanking_isModified = false
	YbConsumeRanking_inTime = false
	
	local curTime = System.getCurrMiniTime()
	if curTime <= 0 then -- 如果是lua引擎没有启动完
		return
	end 
	
	if not IsYbConsumeRankingTime() then
		return
	end
	
	YbConsumeRanking_inTime = true
	-- 定时 3 分钟保存
	System.addTimer("YbConsumeRanking_OnTimer", 3 * 60 * 1000, -1)
		
	print("----------- 开始装载[元宝消耗]排行榜 -----------")		
	
	local file = io.open("data/runtime/ranking/YbConsumeRanking.txt", "r")
	if not file then
		print("没有 [元宝消耗]排行榜 文件：ranking/YbConsumeRanking.txt")
		return
	end
	
	local bom = file:read(3) -- read BOM
	if not bom or string.len(bom) < 3 then
		file:close()
		print("error, 装载 [元宝消耗]排行榜 失败(BOM error)")
		return
	end	

	-- 0:成功并继续解析下一行；1:失败并继续解析下一行；2:旧排行榜数据
	local parseResult = 0

	local line
	while true do
		line = file:read()
		if not line then break end
		
		parseResult = YbConsumeRanking_parse_line(line, parseResult)
		if parseResult == 1 then
			print("error, YbConsumeRanking_parse_line")
			print(line) -- 把信息打印出来，免得覆盖就没了
		end
		
	end

	file:close()
	
	local count = #YbConsumeRanking
	if count == 0 then
		print("装载 [元宝消耗]排行榜 成功, 上榜人数0")
		return
	end
		
	-- 排序，从大到小
	table.sort(YbConsumeRanking, YbConsumeRanking_comps)
	
	if count > 20 then
		for i=count, 21, -1 do
			table.remove(YbConsumeRanking)
		end
		
		count = 20
		YbConsumeRanking_isModified = true
	end
	
	YbConsumeRanking_minYb = YbConsumeRanking[count].yb
	
	print("装载 [元宝消耗]排行榜 成功, 上榜人数"..count)

end

function YbConsumeRanking_save()
	if not YbConsumeRanking_isModified then return end
	
	-- 没有文件则新建，有则清空文件再写		
	local file = io.open("data/runtime/ranking/YbConsumeRanking.txt", "w")
	if not file then
		print("------------ error, open YbConsumeRanking.txt fail")
		return
	end
	
	local curTime = System.getCurrMiniTime() -- 保存时间，以便检查是否跨天
	if curTime <= 0 then return end
	
	local header = "time=" .. tostring(curTime) .. "#ActorId#Yb#ActorName\n"

	file:write(string.format("%c%c%c", 0xEF, 0xBB, 0xBF)) -- write BOM	
	file:write(header) -- 写标题

	local count = 0
	for i, v in ipairs(YbConsumeRanking) do
		if YbConsumeRanking_write_line(file, v) then
			count = count + 1
			if count >= 20 then
				break
			end
		end	
	end
	
	file:close()	
	
	YbConsumeRanking_isModified = false	
	
	if count > 0 then
		YbConsumeRanking_minYb = YbConsumeRanking[count].yb
	else
		YbConsumeRanking_minYb = 0
	end
	
	print("保存 [元宝消耗]排行榜 成功, 上榜人数"..count)
end

-- 排序算法
function YbConsumeRanking_comps(a, b)
	return a.yb > b.yb
end

-- 解析时间，失败返回 0
function YbConsumeRanking_parse_time(line)
	local pos1 = string.find(line, "time=", 1) 
	if not pos1 then return 0 end
	
	local pos2 = string.find(line, '#', pos1 + 5) 
	if not pos2 then return 0 end
	
	local substr = string.sub(line, pos1 + 5, pos2 - 1)
	local time = tonumber(substr)
	return time
end

function YbConsumeRanking_is_old_data(line)	
	local time = YbConsumeRanking_parse_time(line)
	if time <= 0 then return true end	
	
	-- 如果是lua引擎没有启动完
	local curTime = System.getCurrMiniTime()
	if curTime <= 0 then
		print("----- error YbConsumeRanking_is_old_data lua引擎没有启动完")
		return false
	end

	if System.isSameDay(time, curTime) then
		return false
	end

	return true
end

-- 返回值：0:成功并继续解析下一行；1:失败并继续解析下一行；2:旧排行榜数据
function YbConsumeRanking_parse_line(line, lastParseResult)

	if line == "" then return lastParseResult end	
	
	-- ActorId Yb ActorName
	-- 如果玩家名字含有 '\t' 呢？所以这里一个一个分隔
	
	local pos = string.find(line, '\t', 1) 
	if not pos then
		-- 当标题解析
		if YbConsumeRanking_is_old_data(line) then
			return 2
		else
			return 0
		end
	end
	
	if lastParseResult == 2 then -- 旧排行榜数据
		return 2
	end
	
	local substr = string.sub(line, 1, pos - 1)
	local actorId = tonumber(substr)
	if actorId <= 0 then return 1 end
	
	local i = pos + 1
	pos = string.find(line, '\t', i)
	if not pos then return 1 end
	
	substr = string.sub(line, i, pos - 1)
	local _yb = tonumber(substr)
	if _yb <= 0 then return 1 end
	
	-- 剩下的就都是名称
		
	local item = {id=actorId, yb=_yb, name=string.sub(line, pos + 1)}
	
	table.insert(YbConsumeRanking, item)
	return 0
end

function YbConsumeRanking_write_line(file, item)
	if item.id <= 0 or item.yb <= 0 then return false end

	local line = string.format("%d	%d	%s\n", item.id, item.yb, item.name)			
	file:write(line)

	return true
end

function YbConsumeRanking_check_size()
	-- 保险点，重新取数组大小
	local count = #YbConsumeRanking
	if count <= 20 then return end
	
	for i=count, 21, -1 do			
		table.remove(YbConsumeRanking)
	end
	
	count = 20
	YbConsumeRanking_isModified = true	
	YbConsumeRanking_minYb = YbConsumeRanking[count].yb
end


-- 查找插入的位置，返回: 新位置（0则不需要插入），旧位置（0则不在）, 数组大小(新位置不等于0时有效)
function YbConsumeRanking_find_insert_pos(actorId, yb)
	if yb <= 0 then return 0,0,0 end
	
	local newPos = 0
	local oldPos = 0
	local count = 0
	for i, v in ipairs(YbConsumeRanking) do
		if actorId == v.id then
			oldPos = i
		end
		
		if yb > v.yb and newPos == 0 then
			newPos = i
		end
		
		count = count + 1
	end
	
	if count < 20 and newPos == 0 then
		newPos = count + 1
	end
	
	return newPos, oldPos, count
end

-- -----------------------------------------------------

function YbConsumeRanking_SendMyData(sysarg)
	if not YbConsumeRanking_inTime then return end
	
	local netPack = DataPack.allocPacket(sysarg, 139, LogicCmd.miscSys.sub.sPublicTest)
	DataPack.writeByte(netPack, 3) -- S2C_PublicTestMsgId.MyYbRankData
	
	local var = Actor.getStaticVar(sysarg)
	
	local dayConsumeYB = var.dayConsumeYB
	if dayConsumeYB == nil then dayConsumeYB = 0 end
	
	local ranking = 0
	if dayConsumeYB > 0 and dayConsumeYB >= YbConsumeRanking_minYb then
		local actorId = Actor.getIntProperty(sysarg, PROP_ENTITY_ID)
		
		for i, v in ipairs(YbConsumeRanking) do
			if actorId == v.id then
				ranking = i
				break
			end
		end
	end
	
	DataPack.writeByte(netPack, ranking)	
	DataPack.writeUInt(netPack, dayConsumeYB)
	
	DataPack.flush(netPack)	
end

function YbConsumeRanking_SendRankingData(sysarg, rankBegin, count)
	if not YbConsumeRanking_inTime then return end
	
	if rankBegin < 1 or rankBegin > 20 or count <= 0 then
		return
	end
		
	local listCount = #YbConsumeRanking
	if (rankBegin + count) > (listCount + 1) then
		count = (listCount + 1) - rankBegin
	end
	
	local netPack = DataPack.allocPacket(sysarg, 139, LogicCmd.miscSys.sub.sPublicTest)
	DataPack.writeByte(netPack, 4) -- S2C_PublicTestMsgId.YbRankData
			
	DataPack.writeByte(netPack, rankBegin)
	DataPack.writeByte(netPack, count)
		
	for i = rankBegin, (rankBegin + count - 1) do
		local v = YbConsumeRanking[i]
		DataPack.writeString(netPack, v.name)
		DataPack.writeUInt(netPack, v.yb)
	end
		
	DataPack.flush(netPack)
end

-- 服务器跨天
function OnYbConsumeRankingNewDayArrive()	
	if not YbConsumeRanking_inTime then
		if IsYbConsumeRankingTime() then -- 活动时间到来了
			YbConsumeRanking = {} -- 清空旧数据
			YbConsumeRanking_minYb = 0
			YbConsumeRanking_isModified = true
			YbConsumeRanking_inTime = true
	
			System.addTimer("YbConsumeRanking_OnTimer", 3 * 60 * 1000, -1)
		end
		
		return
	end
				
	-- 活动时间结束
	print("发送元宝消耗排行榜离线邮件")
	
	for i, v in ipairs(YbConsumeRanking) do
		if i == 1 then
			SendScriptOfflineMsg(v.id, "领取元宝消耗排行榜第1名奖励", nil, OfflineMsgIds.YbConsumeRanking1)
		elseif i <= 5 then
			SendScriptOfflineMsg(v.id, "领取元宝消耗排行榜第2~5名奖励", nil, OfflineMsgIds.YbConsumeRanking2)
		elseif i <= 20 then
			SendScriptOfflineMsg(v.id, "领取元宝消耗排行榜第6~20名奖励", nil, OfflineMsgIds.YbConsumeRanking3)
		end
		
		print("第".. i .. "名,id=" .. v.id .. ",name=" .. v.name .. ",yb=" .. v.yb)
	end
	
	YbConsumeRanking = {} -- 清空，避免下次装载到旧数据
	YbConsumeRanking_isModified = true
	YbConsumeRanking_save()		
	
	YbConsumeRanking_minYb = 0
	YbConsumeRanking_isModified = false
	
	YbConsumeRanking_inTime = IsYbConsumeRankingTime()	
	
	if YbConsumeRanking_inTime then
		if not System.hasTimer("YbConsumeRanking_OnTimer") then
			System.addTimer("YbConsumeRanking_OnTimer", 3 * 60 * 1000, -1)
		end
	else
		System.deleteTimer("YbConsumeRanking_OnTimer")
		print("元宝消耗排行榜活动 结束")
	end
end

function YbConsumeRanking_OnTimer(leftTimes)
	YbConsumeRanking_save()
end

function YbConsumeRankingConsumeYb(sysarg, totalYb)
	if totalYb <= 0 then return end
	
	local actorId = Actor.getIntProperty(sysarg, PROP_ENTITY_ID)	
		
	local newPos, oldPos, count = YbConsumeRanking_find_insert_pos(actorId, totalYb)
	if newPos == 0 then
		return
	end	

	local actorName = Actor.getName(sysarg)	
	YbConsumeRanking_isModified = true
	
	if newPos == oldPos then
		YbConsumeRanking[oldPos].yb = totalYb		
	else	
		local item = {id=actorId, yb=totalYb, name=actorName}
		table.insert(YbConsumeRanking, newPos, item)
		
		if oldPos > 0 then
			if newPos <= oldPos then
				table.remove(YbConsumeRanking, oldPos + 1)
			else
				table.remove(YbConsumeRanking, oldPos)
			end
		else
			count = count + 1 -- 新增
		end	
	end

	YbConsumeRanking_minYb = YbConsumeRanking[count].yb
		
	-- 删除挤出排行榜的
	if count > 20 then
		YbConsumeRanking_check_size()
	end

	-- 提示
	if newPos ~= oldPos and newPos >= 1 and newPos <= 20 then		
		local msg = string.format("玩家{color;FF00FF00;%s}元宝消耗排行第%d！", actorName, newPos)
		System.broadcastTipmsg(msg, ttScreenCenter)
	end	
end

function OnRankingConsumeYbEvent(sysarg, dayConsumeYB)	
	if not YbConsumeRanking_inTime then	return end
	
	-- 消耗的总元宝数是否可以上榜了？
	if dayConsumeYB < PublicTestCfg.YbConsumeRanking.minConsume then return end

	YbConsumeRankingConsumeYb(sysarg, dayConsumeYB)
end
	
-- 返回 true 则删除该消息，返回 false 则不删除
function OfflineMsgGiveYbConsumeRankingAward(sysarg, msgId, strParams)
	local award = PublicTestCfg.YbConsumeRanking.awardList[msgId]
	if award == nil then return false end
	
	if Awards.Give(sysarg, award, LogIds.YbConsumeRankingAward, tostring(msgId)) == false then
		return false
	end
	
	return true
end

